﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Routing;

namespace PoliticalClassifier.Web
{
	/// <summary>
	/// Adds support for the HTML file input element in an ASP.NET MVC view.
	/// </summary>
	public static class FileHtmlHelperExtensions
	{
		#region Public Extension Methods

		/// <summary>
		/// Returns an HTML file input element for the property in the object that is represented by the specified expression.
		/// </summary>
		/// <typeparam name="TModel">The type of the model.</typeparam>
		/// <typeparam name="TValue">The type of the property.</typeparam>
		/// <param name="html">The HTML helper instance that this method extends.</param>
		/// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
		/// <returns>An HTML file input element for the property in the object that is represented by the specified expression.</returns>
		public static MvcHtmlString FileFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
		{
			return FileHelper(html, ModelMetadata.FromLambdaExpression(expression, html.ViewData), ExpressionHelper.GetExpressionText(expression));
		}


		/// <summary>
		/// Returns an HTML file input element for the property in the object that is represented by the specified expression, using the specified HTML attributes.
		/// </summary>
		/// <typeparam name="TModel">The type of the model.</typeparam>
		/// <typeparam name="TValue">The type of the property.</typeparam>
		/// <param name="html">The HTML helper instance that this method extends.</param>
		/// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
		/// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
		/// <returns>An HTML file input element for the property in the object that is represented by the specified expression.</returns>
		public static MvcHtmlString FileFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
		{
			return FileHelper(html, ModelMetadata.FromLambdaExpression(expression, html.ViewData), ExpressionHelper.GetExpressionText(expression), new RouteValueDictionary(htmlAttributes));
		}


		/// <summary>
		/// Returns an HTML file input element for the property in the object that is represented by the specified expression, using the specified HTML attributes.
		/// </summary>
		/// <typeparam name="TModel">The type of the model.</typeparam>
		/// <typeparam name="TValue">The type of the property.</typeparam>
		/// <param name="html">The HTML helper instance that this method extends.</param>
		/// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
		/// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
		/// <returns>An HTML file input element for the property in the object that is represented by the specified expression.</returns>
		public static MvcHtmlString FileFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
		{
			return FileHelper(html, ModelMetadata.FromLambdaExpression(expression, html.ViewData), ExpressionHelper.GetExpressionText(expression), htmlAttributes);
		}

		#endregion

		#region Private Helper Methods

		/// <summary>
		/// Returns an HTML file input element using the specified HTML attributes.
		/// </summary>
		/// <param name="html">The HTML helper instance.</param>
		/// <param name="metaData">The metadata for the property to render.</param>
		/// <param name="htmlFieldName">The HTML field name attribute.</param>
		/// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
		/// <returns>An HTML file input element.</returns>
		private static MvcHtmlString FileHelper(HtmlHelper html, ModelMetadata metaData, string htmlFieldName, IDictionary<string, object> htmlAttributes = null)
		{
			TagBuilder tag = new TagBuilder("input");
			tag.MergeAttributes(htmlAttributes);
			tag.MergeAttribute("type", "file", true);
			tag.MergeAttribute("name", htmlFieldName, true);
			tag.GenerateId(htmlFieldName);
			ModelState state;

			// Mark invalid field with the invalid css class
			if (html.ViewData.ModelState.TryGetValue(htmlFieldName, out state) && (state.Errors.Count > 0))
				tag.AddCssClass(HtmlHelper.ValidationInputCssClassName);

			tag.MergeAttributes(html.GetUnobtrusiveValidationAttributes(htmlFieldName, metaData));
			return MvcHtmlString.Create(tag.ToString(TagRenderMode.SelfClosing));
		}

		#endregion
	}
}